<!DOCTYPE html>
<html>
<!--
Copyright 2008 The Closure Library Authors. All Rights Reserved.

Use of this source code is governed by an Apache 2.0 License.
See the COPYING file for details.
-->
<head>
<title>Closure Unit Tests - goog.format.HtmlPrettyPrinter</title>
<script src="../base.js"></script>
<script>
  goog.require('goog.format.HtmlPrettyPrinter');
  goog.require('goog.testing.MockClock');
  goog.require('goog.testing.jsunit');
</script>
</head>
<body>
<script>
var COMPLEX_HTML = '<!DOCTYPE root-element [SYSTEM OR PUBLIC FPI] "uri" [' +
    '<!-- internal declarations -->]>' +
    '<html><head><title>My HTML</title><!-- my comment --></head>' +
    '<body><h1>My Header</h1>My text.<br><b>My bold text.</b><hr>' +
    '<pre>My\npreformatted <br> HTML.</pre>5 < 10</body>' +
    '</html>';
var mockClock;
var mockClockTicks;

function setUp() {
  mockClockTicks = 0;
  mockClock = new goog.testing.MockClock();
  mockClock.getCurrentTime = function() {
    return mockClockTicks++;
  };
  mockClock.install();
}

function tearDown() {
  if (mockClock) {
    mockClock.uninstall();
  }
}

function testSimpleHtml() {
  var actual = goog.format.HtmlPrettyPrinter.format('<br><b>bold</b>');
  assertEquals('<br>\n<b>bold</b>\n', actual);
  assertEquals(actual, goog.format.HtmlPrettyPrinter.format(actual));
};

function testSimpleHtmlMixedCase() {
  var actual = goog.format.HtmlPrettyPrinter.format('<BR><b>bold</b>');
  assertEquals('<BR>\n<b>bold</b>\n', actual);
  assertEquals(actual, goog.format.HtmlPrettyPrinter.format(actual));
}

function testComplexHtml() {
  var actual = goog.format.HtmlPrettyPrinter.format(COMPLEX_HTML);
  var expected = '<!DOCTYPE root-element [SYSTEM OR PUBLIC FPI] "uri" [' +
    '<!-- internal declarations -->]>\n' +
    '<html>\n' +
    '<head>\n' +
    '<title>My HTML</title>\n' +
    '<!-- my comment -->' +
    '</head>\n' +
    '<body>\n' +
    '<h1>My Header</h1>\n' +
    'My text.<br>\n' +
    '<b>My bold text.</b>\n' +
    '<hr>\n' +
    '<pre>My\npreformatted <br> HTML.</pre>\n' +
    '5 < 10' +
    '</body>\n' +
    '</html>\n';
  assertEquals(expected, actual);
  assertEquals(actual, goog.format.HtmlPrettyPrinter.format(actual));
}

function testTimeout() {
  var pp = new goog.format.HtmlPrettyPrinter(3);
  var actual = pp.format(COMPLEX_HTML);
  var expected = '<!DOCTYPE root-element [SYSTEM OR PUBLIC FPI] "uri" [' +
    '<!-- internal declarations -->]>\n' +
    '<html>\n' +
    '<head><title>My HTML</title><!-- my comment --></head>' +
    '<body><h1>My Header</h1>My text.<br><b>My bold text.</b><hr>' +
    '<pre>My\npreformatted <br> HTML.</pre>5 < 10</body>' +
    '</html>\n';
  assertEquals(expected, actual);
}

function testKeepLeadingIndent() {
  var original = ' <b>Bold</b> <i>Ital</i> ';
  var expected = ' <b>Bold</b> <i>Ital</i>\n';
  assertEquals(expected, goog.format.HtmlPrettyPrinter.format(original));
}

function testTrimLeadingLineBreaks() {
  var original = '\n \t\r\n  \n <b>Bold</b> <i>Ital</i> ';
  var expected = ' <b>Bold</b> <i>Ital</i>\n';
  assertEquals(expected, goog.format.HtmlPrettyPrinter.format(original));
}

function testExtraLines() {
  var original = '<br>\ntombrat';
  assertEquals(original + '\n', goog.format.HtmlPrettyPrinter.format(original));
}

function testCrlf() {
  var original = '<br>\r\none\r\ntwo<br>';
  assertEquals(original + '\n', goog.format.HtmlPrettyPrinter.format(original));
}

function testEndInLineBreak() {
  assertEquals('foo\n', goog.format.HtmlPrettyPrinter.format('foo'));
  assertEquals('foo\n', goog.format.HtmlPrettyPrinter.format('foo\n'));
  assertEquals('foo\n', goog.format.HtmlPrettyPrinter.format('foo\n\n'));
  assertEquals('foo<br>\n', goog.format.HtmlPrettyPrinter.format('foo<br>'));
  assertEquals('foo<br>\n', goog.format.HtmlPrettyPrinter.format('foo<br>\n'));
}

function testTable() {
  var original = '<table>' +
    '<tr><td>one.one</td><td>one.two</td></tr>' +
    '<tr><td>two.one</td><td>two.two</td></tr>' +
    '</table>';
  var expected = '<table>\n' +
    '<tr>\n<td>one.one</td>\n<td>one.two</td>\n</tr>\n' +
    '<tr>\n<td>two.one</td>\n<td>two.two</td>\n</tr>\n' +
    '</table>\n';
  assertEquals(expected, goog.format.HtmlPrettyPrinter.format(original));
}

/**
 * We have a sanity check in HtmlPrettyPrinter to make sure the regex index
 * advances after every match. We should never hit this, but we include it on
 * the chance there is some corner case where the pattern would match but not
 * process a new token. It's not generally a good idea to break the
 * implementation to test behavior, but this is the easiest way to mimic a
 * bad internal state.
 */
function testRegexMakesProgress() {
  var original = goog.format.HtmlPrettyPrinter.TOKEN_REGEX_;

  try {
    // This regex matches \B, an index between 2 word characters, so the regex
    // index does not advance when matching this.
    goog.format.HtmlPrettyPrinter.TOKEN_REGEX_ =
        /(?:\B|<!--.*?-->|<!.*?>|<(\/?)(\w+)[^>]*>|[^<]+|<)/g;

    // It would work on this string.
    assertEquals('f o o\n', goog.format.HtmlPrettyPrinter.format('f o o'));

    // But not this one.
    var success = false;
    try {
      goog.format.HtmlPrettyPrinter.format(COMPLEX_HTML);
    } catch (ex) {
      success = true;
      assertEquals('Regex failed to make progress through source html.',
          ex.message);
    }
    assertTrue('should have failed for invalid regex - endless loop.', success);
  } finally {
    goog.format.HtmlPrettyPrinter.TOKEN_REGEX_ = original;
  }
}

/**
 * FF3.0 doesn't like \n between </li> and </ul>. See bug 1520665.
 */
function testLists() {
  var original = "<ul><li>one</li><ul><li>two</li></UL><li>three</li></ul>";
  var expected =
    "<ul><li>one</li>\n<ul><li>two</li></UL>\n<li>three</li></ul>\n";
  assertEquals(expected, goog.format.HtmlPrettyPrinter.format(original));
}

/**
 * We have a sanity check in HtmlPrettyPrinter to make sure the regex fully
 * tokenizes the string. We should never hit this, but we include it on the
 * chance there is some corner case where the pattern would miss a section of
 * original string. It's not generally a good idea to break the
 * implementation to test behavior, but this is the easiest way to mimic a
 * bad internal state.
 */
function testAvoidDataLoss() {
  var original = goog.format.HtmlPrettyPrinter.TOKEN_REGEX_;

  try {
    // This regex does not match stranded '<' characters, so does not fully
    // tokenize the string.
    goog.format.HtmlPrettyPrinter.TOKEN_REGEX_ =
        /(?:<!--.*?-->|<!.*?>|<(\/?)(\w+)[^>]*>|[^<]+)/g;

    // It would work on this string.
    assertEquals('foo\n', goog.format.HtmlPrettyPrinter.format('foo'));

    // But not this one.
    var success = false;
    try {
      goog.format.HtmlPrettyPrinter.format(COMPLEX_HTML);
    } catch (ex) {
      success = true;
      assertEquals('Lost data pretty printing html.', ex.message)
    }
    assertTrue('should have failed for invalid regex - data loss.', success);
  } finally {
    goog.format.HtmlPrettyPrinter.TOKEN_REGEX_ = original;
  }
}

</script>
</body>
</html>
